import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;


public class Handler implements Runnable{
	
	private class Buffer implements Runnable{
		private Queue<Message> _queue = null;
		
		/**
		 * Constructor
		 */
		public  Buffer(){
			_queue = new ConcurrentLinkedQueue<Message>(); //Create a new messages queue
		}
		
		/**
		 * Receives messages from socket and inserts them into the queue
		 */
		public void run(){
			
			while (!isStop() && _socket.isAlive()) {
				
				//receive next message from socket buffer
				Message msg = _socket.receive();
				
				//check if new message has arrived
				if (msg != null)
					//insert the new message to the buffer 
					_queue.add(msg);			
			}
		
		}
		
		/**
		 * Get the next message
		 * @return
		 */
		public Message nextMessage(){
			return _queue.poll();
		}
	}
	
	
	//Data members
	private ISocket _socket = null;	//Socket that is handled
	
	//Sub handlers - may continue handling the messages
	private UpdateHandler _updateHandler = null;
	private RequestEntryHandler _requestEntryHandler = null;
	private RequestPictureHandler _requestPictureHandler = null;
	private EntryManager _entryManager = null;
	private PictureManager _pictureManager = null;
	
	
	private boolean _flgStop = false; //Indicate whether terminate the program
	private DB _db = null; //DB instance
	
	//Prefixes of the different messages
	private static final String UPDATE_MESSAGE = "update";
	private static final String REQUEST_ENTRY_MESSSAGE = "requestentry";
	private static final String REQUEST_PICTURE_MESSAGE = "requestpicture";
	private static final String ENTRY_MESSAGE = "entry";
	private static final String PICTURE_MESSAGE = "picture";
	
	/**
	 * Constructor
	 * @param socket - socket to be handled
	 * @param db - DB instance
	 * @param pictureManager - instance of the picture manager
	 * @param maxIdleTime - max time to wait between messages
	 * @param MSS - picture MSS size in bytes
	 */
	public Handler(ISocket socket, DB db, PictureManager pictureManager, long maxIdleTime, int MSS) {
		_socket = socket;
		_db = db;
		_pictureManager = pictureManager;
		_entryManager = new EntryManager(_db, maxIdleTime, _pictureManager);
		_requestEntryHandler = new RequestEntryHandler(_socket, _db);
		_updateHandler = new UpdateHandler(_db, _entryManager, _socket, _pictureManager);
		_requestPictureHandler = new RequestPictureHandler(socket, MSS, _db);
	}
	
	public synchronized void stop() {
		_flgStop = true;
	}
	private synchronized boolean isStop() {return _flgStop;} 
	
	public void run() {start();}
	
	public void start() {
		
		//Start request entry handler
		new Thread(_requestEntryHandler, "Request Entry Handler").start();
		
		//Initialize Buffer
		Buffer buffer = new Buffer();
		
		//start the buffer in a new thread
		new Thread(buffer, "Handler Buffer").start();
		
		//Listen to incoming messages
		while (!isStop() && _socket.isAlive()) {
			//DatagramPacket packet = new DatagramPacket(new byte[_maxMessageLength],_maxMessageLength); 
			//Message msg = _socket.receive();
			Message msg = buffer.nextMessage();
			if (msg != null)
				analyzePacket(msg);//Analyze packet
			
		}
			
		//Deallocate resources
		_requestPictureHandler.stop();
		_requestEntryHandler.stop();
		_socket.close();
	}
	
	private void analyzePacket(Message msg) {
		String data = new String(msg.getData());
		String fromPeer = null;
		
		//Log incoming data
		if (data.toLowerCase().startsWith(UPDATE_MESSAGE)) 
			fromPeer = Utils.parseMessage(new String(msg.getData()))[1]; //Get peer name
		else
			fromPeer = SessionsManager.getNameBySource(new Source(_socket, msg.getAddress(), msg.getPort())); 
		
		if (fromPeer == null)
			fromPeer = "unknown";
		
		Record rec = _db.getRecord(_db.get_OwnerName());
		String localIP = "unknown";
		if (rec != null)
			localIP = rec.get_ip();
		
		String logData = String.format("%s\nfrom:%s\nip:%s\n%s:%s\nto:%s\nip:%s\n%s:%s", 
				new String(msg.getData()), fromPeer,
				Utils.InetToString(msg.getAddress()), _socket.type().toLowerCase(),
				msg.getPort(), _db.get_OwnerName(),
				localIP, _socket.type().toLowerCase(),
				_socket.getLocalPort());
			
		Proj350.logger.logIncoming(logData);
		
		if (data.toLowerCase().startsWith(UPDATE_MESSAGE)) {
			_updateHandler.handle(msg);
		} else if (data.toLowerCase().startsWith(REQUEST_ENTRY_MESSSAGE)) {
			_requestEntryHandler.handle(msg);
		} else if (data.toLowerCase().startsWith(REQUEST_PICTURE_MESSAGE)) {
			_requestPictureHandler.handle(msg);
		} else if (data.toLowerCase().startsWith(ENTRY_MESSAGE)){
			_entryManager.handle(msg);
		} else if (data.toLowerCase().startsWith(PICTURE_MESSAGE)){
			_pictureManager.handle(msg);
		}
	}
}
